package com.apes.scm.stock.profitAndLoss;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.generator.service.SequenceService;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.plugin.outbox.OutboxSubscriberService;
import com.apes.framework.plugin.outbox.annotation.OutboxSubscriber;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.service.ProductAccountService;
import com.apes.scm.account.tax.model.Tax;
import com.apes.scm.invoice.model.Invoice;
import com.apes.scm.invoice.model.InvoiceItem;
import com.apes.scm.invoice.service.InvoiceService;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.repository.ProductRepository;
import com.apes.scm.rbac.repository.UserRepository;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import com.apes.scm.voucher.service.VoucherService;
import com.apestech.framework.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class ProductProfitService extends DomainService {

    @Autowired
    private ProductAccountService productAccountService;
    @Autowired
    private ProfitAndLossRepository profitAndLossRepository;
    @Autowired
    private SequenceService sequenceService;

    /**
     * 创建
     * @param request
     * @return
     */
    public ProfitAndLoss create(SimpleRequest request) {
        ProfitAndLoss profitAndLoss = request.getO(ProfitAndLoss.class);
        profitAndLoss.setState("sent");
        profitAndLoss.setCreator(profitAndLoss.getOperator());
        String companyId = request.getCurrentCompany() == null ? "" : ((PartyRole)request.getCurrentCompany()).getId();
        profitAndLoss.setId("BY" + companyId + sequenceService.getId(ProfitAndLoss.CONST_TABLE_NAME));
        profitAndLoss.getProfitAndLossItems().forEach(item -> item.setQty(Math.abs(item.getQty())));
        return profitAndLossRepository.saveAndFlush(profitAndLoss);
    }


    /**
     * 功能: 修改
     * @param request
     * @return
     */
    public ProfitAndLoss update(SimpleRequest request) {
        isValid(request, profitAndLossRepository);
        ProfitAndLoss profitAndLoss = request.getO(ProfitAndLoss.class);
        return profitAndLossRepository.saveAndFlush(profitAndLoss);
    }

    /**
     * 功能：删除订单
     */
    public ProfitAndLoss delete(SimpleRequest request) {
        isValid(request, profitAndLossRepository);
        ProfitAndLoss profitAndLoss = request.getO(ProfitAndLoss.class);

        profitAndLoss.setState("delete");
        profitAndLoss.setDeleteId(getPerson(request));
        profitAndLoss.setDeleteDate(new Date());

        return profitAndLossRepository.saveAndFlush(profitAndLoss);
    }

    public ProfitAndLoss approve(SimpleRequest request){
        isValid(request, profitAndLossRepository);
        ProfitAndLoss profitAndLoss = request.getO(ProfitAndLoss.class);
        profitAndLoss.setState("approve");
        profitAndLoss.setApproveUid(getPerson(request));
        profitAndLoss.setDateApprove(new Date());
        profitAndLoss = profitAndLossRepository.save(profitAndLoss);


        JSONObject voucherParam = new JSONObject();
        voucherParam.put("id", profitAndLoss.getId());
        SpringManager.getBean(OutboxSubscriberService.class)
                .commonSendRequest("local:productProfitService.writeVoucherOutbox", profitAndLoss.getId(), voucherParam);
        return profitAndLoss;
    }


    // 审批 写入凭证
    public void writeVoucher(String id) {
        if (SpringManager.getBean(VoucherRepository.class).findOne(JpaDsl.toCriteriaByEq("source", id)).orElse(null) != null) throw new RuntimeException("请勿重复执行！");
        ProfitAndLoss profitAndLoss = profitAndLossRepository.findOne(id);

        Voucher voucher = getVoucher(profitAndLoss);
        SpringManager.getBean(VoucherService.class).save(voucher);
        SpringManager.getBean(InvoiceService.class).save(getInvoiced(voucher));
    }

    @OutboxSubscriber(id="local:productProfitService.writeVoucherOutbox", name="报溢写入凭证")
    public void writeVoucherOutbox(OutboxPayloadWrapper outboxPayloadWrapper) {
        this.writeVoucher(JSONObject.parseObject(outboxPayloadWrapper.getPayload()).getString("id"));
    }



    /**
     * 写入商品可用数
     * @param profitAndLoss 损益单
     */
    private void setProductCostQty(ProfitAndLoss profitAndLoss) {
        profitAndLoss.getProfitAndLossItems().stream().forEach(
                lossItem -> {

                    // 写入可用数
                    productAccountService.setProductAvailable(lossItem.getProduct(),
                            profitAndLoss.getLocation(), lossItem.getUom(),
                            lossItem.getQty(),
                            profitAndLoss.getInnerBusinessType(),
                            profitAndLoss.getId(), lossItem.getId(),
                            "报溢单审核", profitAndLoss.getCompany());

                    // 写入保管数
                    productAccountService.setProductCustody(lossItem.getProduct(),
                            profitAndLoss.getLocation(), lossItem.getUom(),
                            Math.abs(lossItem.getQty()),
                            profitAndLoss.getInnerBusinessType(),
                            profitAndLoss.getId(), lossItem.getId(),
                            "报溢单审核", profitAndLoss.getCompany());
                });
    }

    /**
     * 验证数据合法性
     * @param request 请求参数
     * @param repository 当前的数据层访问参数
     * @return
     */
    private void isValid(SimpleRequest request, CustomRepository repository){
        Model oldModel = (Model)  repository.findOne((String) request.get("id"));
        if (oldModel == null) throw new RuntimeException("订单【" + request.get("id") + "】在系统中不存在。");
        if (oldModel.getVersion() != (int) request.get("version")) throw new RuntimeException("记录已更新或已删除，请重新提交！");
    }

    private Voucher getVoucher(ProfitAndLoss productLoss) {
        Voucher voucher = new Voucher();
        // 主表信息
        voucher.setCompany(productLoss.getCompany());
        voucher.setDept(productLoss.getLocation().getDepot().getDept());
        voucher.setPartner(productLoss.getResponsibility());
        voucher.setContract(productLoss.getContract());
        voucher.setInventoryMode(InventoryMode.IM_OWN);     //设置库存模式：自有
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：经销

        // 凭证模板必配字段
        voucher.setBusinessType(BusinessType.PURCHASE_ORDER);
        voucher.setOrigin(StringUtil.isEmpty(productLoss.getSource()) ? productLoss.getId() : productLoss.getSource());
        voucher.setOriginBusinessType(StringUtil.isEmpty(productLoss.getSourceBusinessType()) ? productLoss.getInnerBusinessType() : productLoss.getSourceBusinessType());
        voucher.setSource(productLoss.getId());
        voucher.setSourceBusinessType(productLoss.getInnerBusinessType());

        // 从表信息
        Set<VoucherItem> voucherItems = new HashSet();
        productLoss.getProfitAndLossItems().stream()
                .forEach(items -> {
                    // 写入账目
                    this.writeStockAccount(productLoss, items);

                    VoucherItem voucherItem = new VoucherItem();
                    voucherItem.setVoucher(voucher);
                    voucherItem.setProduct(items.getProduct());
                    voucherItem.setTax(items.getTax());
                    voucherItem.setOwner(productLoss.getCompany());

                    // 设置为销售的单位以及单价
                    voucherItem.setUom(items.getUom());
                    voucherItem.setProductQty(items.getQty());
                    voucherItem.setPriceUnit(items.getPriceUnit());

                    voucherItem.setOriginItemId(items.getId());
                    voucherItem.setSourceItemId(items.getSourceItemId());
//                    voucherItem.setSupplier(productLoss.getResponsibility());
//                    voucherItem.setInventoryBatch(items.getInventoryBatch());

                    voucherItems.add(voucherItem);
                });
        voucher.setVoucherItems(voucherItems);
        return voucher;
    }

    // 可用数 + 保管数
    private void writeStockAccount(ProfitAndLoss profitAndLoss, ProfitAndLossItem lossItem) {
        // 写入可用数
        productAccountService.setProductAvailable(lossItem.getProduct(),
                profitAndLoss.getLocation(), lossItem.getUom(),
                lossItem.getQty(),
                profitAndLoss.getInnerBusinessType(),
                profitAndLoss.getId(), lossItem.getId(),
                "报溢单审核", profitAndLoss.getCompany());

        // 写入保管数
        productAccountService.setProductCustody(lossItem.getProduct(),
                profitAndLoss.getLocation(), lossItem.getUom(),
                Math.abs(lossItem.getQty()),
                profitAndLoss.getInnerBusinessType(),
                profitAndLoss.getId(), lossItem.getId(),
                "报溢单审核", profitAndLoss.getCompany());
    }


    /**
     * 创建账单
     * @param voucher
     */
    private Invoice getInvoiced(Voucher voucher) {
        Invoice invoice = new Invoice();
        invoice.setCreator(voucher.getCreator());
        invoice.setCompany(voucher.getCompany());
        invoice.setPartner(voucher.getPartner());
        invoice.setDept(voucher.getDept());
        invoice.setContract(voucher.getContract());
        invoice.setInventoryMode(voucher.getInventoryMode());
        invoice.setAccountingMethod(voucher.getAccountingMethod());
        invoice.setOriginBusinessType(voucher.getOriginBusinessType());
        invoice.setOrigin(voucher.getOrigin());
        invoice.setSourceBusinessType(voucher.getInnerBusinessType());
        invoice.setSource(voucher.getId());
        invoice.setCustomerExpand(voucher.getCustomerExpand());
        invoice.setInvoiceItems(voucher.getVoucherItems().stream().map(voucherItem -> {
            InvoiceItem invoiceItem = new InvoiceItem();
            invoiceItem.setProduct(voucherItem.getProduct());
            invoiceItem.setPriceUnit(voucherItem.getPriceUnit());
            invoiceItem.setQty(voucherItem.getProductQty());
            invoiceItem.setUom(voucherItem.getUom());
            invoiceItem.setTax(voucherItem.getTax());
            invoiceItem.setOriginItemId(voucherItem.getOriginItemId());
            invoiceItem.setSourceItemId(String.valueOf(voucherItem.getId()));
            return invoiceItem;
        }).collect(Collectors.toSet()));
        return invoice;
    }

    /************************OA****************************************/
    public ProfitAndLoss profitOA(SimpleRequest simpleRequest) {
        ProfitAndLoss profitAndLoss = simpleRequest.getO(ProfitAndLoss.class);
        profitAndLoss.setState("OA");
        String creatorId = profitAndLoss.getCreator();
        profitAndLoss.setOAUser(SpringManager.getBean(UserRepository.class).findAll(JpaDsl.toCriteriaByEq("person.id", creatorId)).get(0).getId().toString());
        return profitAndLossRepository.saveAndFlush(profitAndLoss);
    }

    /**
     * 报溢单导入
     * @param request
     * @return
     */
    public JSONObject beImport(SimpleRequest request) {
        //Excel信息
        JSONObject importData = request.get("importData");
        JSONArray field = (JSONArray) request.get("data");
        JSONObject exclObject = new JSONObject();                  //返回值

        JSONArray exclArr = new JSONArray();            //行内容
        JSONArray headerName = new JSONArray();         //头部
        JSONArray fieldName = new JSONArray();          //field
        for (Object o : field) {
            JSONObject webTab = (JSONObject) o;
            headerName.add(webTab.get("name"));
            fieldName.add(webTab.get("field"));
        }

        //获取model
        JSONObject model = (JSONObject) request.get("model");
        String companyId = model.getJSONObject("company").getString("id");
        Tax tax = JSONObject.parseObject(model.getString("tax"), Tax.class);

        //业务逻辑
        importData.forEach((k, v) -> {
            //获得value ===Excel内容
            JSONArray excl = (JSONArray) v;
            JSONArray header = (JSONArray) excl.get(0); //首行数据
            String productId;
            for (int i = 0; i < excl.size(); i++) {  //遍历每一行

                if (i == 0) { //跳过首行
                    exclArr.add(headerName);
                    continue;
                }

                JSONArray lineLong = (JSONArray) excl.get(i); //当前行
                if (lineLong.size() == 0) {
                    continue; //跳过空白行
                }
                //合并2个list为一个map,KEY为首行数据，Value为当前行数据
                Map<String, String> rst = IntStream.range(0, header.size()).collect(HashMap::new,
                        (m, key) -> m.put(header.getString(key).replace(";", ""),
                                key >= lineLong.size() ? "" : (lineLong.getString(key) == null ? null : lineLong.getString(key).trim().replaceAll("[\\s]{2,}", " "))),
                        (m1, m2) -> {
                        });
                productId = rst.get("商品");
                double qty = Double.parseDouble(rst.get("报溢数量"));
                Product product = SpringManager.getBean(ProductRepository.class).findOne(productId);
                Optional.ofNullable(product).orElseThrow(() -> new RuntimeException(String.format("商品[%s]不存在", rst.get("商品"))));

                JSONObject price = getNewPrice(productId);
                if (Objects.isNull(price))price = getNewPrice(productId,companyId);
                double JG = price.getDoubleValue("P001");
                DecimalFormat df = new DecimalFormat("#0.00");
                //用一个有序的map 封装数据, 防止乱序
                Map<String, Object> map = new LinkedHashMap<>();
                map.put("序号", "");
                map.put("订单行号", "");
                map.put("来源单明细单号", "");
                map.put("商品", MapUtil.mapper("id", product.getId(), "name", product.getName()));
                map.put("商品名称", MapUtil.mapper("id", product.getId(), "name", product.getName()));
                map.put("单位", MapUtil.mapper("id", product.getUom().getId(), "name", product.getUom().getName()));
                map.put("报溢数量", qty);
                map.put("单价", JG==0?"":df.format(JG));
                map.put("金额", JG==0?"":df.format(JG*qty));
                map.put("税率", tax);
                map.put("备注", "");
                //将map 转换成 list 利用stream流 引用Map类的getValue方法,弄成一个list
                List<Object> valueList = new ArrayList<>(map.values());
                exclArr.add(valueList);
            }

            exclObject.put(k, exclArr);
        });
        return exclObject;
    }

    public JSONObject getProductPrice(SimpleRequest request) {
        JSONObject result = getNewPrice(request.getJO().getString("SPID"));
        if (Objects.isNull(result))result = getNewPrice(request.getJO().getString("SPID"), request.getJO().getString("GSID"));
        double p001 = result.getDoubleValue("P001");
        if (p001 == 0) throw new RuntimeException("采购价格文件不能为0！商品编码：" + request.getJO().getString("SPID"));
        return result;
    }

    /**
     *  采购价格文件
     * @param productId 商品
     * @return
     */
    public JSONObject getNewPrice(String productId){
        JSONObject condi = new JSONObject();
        condi.put("SYS_RQ", new Date());
        condi.put("conditionStrategyId", "CG01");
        condi.put("COMPANY_ID", "");
        condi.put("CAR_LICENSE", "");
        condi.put("PRODUCT_ID", productId);
        condi.put("SYS_NO_CHECK", "SUPPLIER_ID,CITY");
        Object obj;
        try {
            obj = this.invoke("condition.execConditionStrategy", condi);
        }catch (Exception e) {
            return null;
        }
        return new JSONObject(MapUtil.mapped(obj));
    }

    /**
     * 商品+供应商+公司
     * @param productId 商品
     * @param companyId 公司
     * @return
     */
    public JSONObject getNewPrice(String productId, String companyId){
        JSONObject condi = new JSONObject();
        condi.put("SYS_RQ", new Date());
        condi.put("conditionStrategyId", "CG01");
        condi.put("COMPANY_ID", companyId);
        condi.put("CAR_LICENSE", "");
        condi.put("PRODUCT_ID", productId);
        condi.put("SYS_NO_CHECK", "SUPPLIER_ID");
        Object obj;
        try {
            obj = this.invoke("condition.execConditionStrategy", condi);
        }catch (Exception e) {
            throw new RuntimeException("采购价格文件不能为0！商品编码：" + productId);
        }
        return new JSONObject(MapUtil.mapped(obj));
    }
}
